package com.aps.algorithm.heuristic.genetic;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.SerializeUtil;
import com.aps.algorithm.heuristic.genetic.dto.ApsOperationDto;
import com.aps.algorithm.heuristic.genetic.model.AbstractInitializePopulation;
import com.aps.algorithm.heuristic.genetic.model.Chromosome;
import com.aps.algorithm.utils.AlgorithmUtils;
import com.aps.algorithm.utils.output.OutputUtils;
import com.aps.common.model.OperationAbandoned;
import com.aps.common.model.genetic.OrderProcess;
import lombok.extern.slf4j.Slf4j;
import org.springframework.objenesis.instantiator.util.ClassUtils;
import org.springframework.util.Assert;

import java.security.SecureRandom;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author nbZhuozh
 * @Date 2025-09-08  16:10
 *
 *
 * - 染色体类型定义 、 基因类型定义
 * - 初始化基因序列
 * - 【全排列】返回对应 染色体种群
 * - 【选择部分】 按照 工序的顺序 （硬约束） 返回对应随机排序
 **/
@Slf4j
public class ApsDefaultInitializePopulation extends AbstractInitializePopulation<ApsOperationDto> {

    /**
     * NOTES ： 【说明】：当前应该为所有订单的所有工序 ； 这个初始化应该是所有基因的集合
     */
    private List<OrderProcess> orderProcesses;

    public ApsDefaultInitializePopulation(List<OrderProcess> orderProcesses) {
        super();
        this.orderProcesses = orderProcesses;
    }
    public ApsDefaultInitializePopulation(List<OrderProcess> orderProcesses, long _populationSize , long _max_permutation_threshold) {
        super(_populationSize , _max_permutation_threshold);
        this.orderProcesses = orderProcesses;
    }

    /**
     * 按照订单生成 对应默认基因序列
     * @return
     */
    @Override
    public List<ApsOperationDto> generateGeneSequence() {
        Assert.notNull(orderProcesses, "orders must not be null");
        return new ArrayList<>(ApsOperationDto.build(orderProcesses));
    }

    /**
     * 全排列 、 部分抽取  逻辑处理
     * @param genes
     * @param strategy
     * @return
     */
    @Override
    public List<Chromosome<ApsOperationDto>> generatePopulation(List<ApsOperationDto> genes, InitialPopulationType strategy) {

        return  switch (strategy) {
            //【全排列】
            case ALL -> generateAllPermutations(genes);
            //【随机 + 插入排序】
            case PART -> generateRandomPopulation(genes);
            default -> throw new IllegalStateException("Unexpected value: " + strategy);
        };
    }



    //全排列返回 对应染色体 种群
    private List<Chromosome<ApsOperationDto>> generateAllPermutations(List<ApsOperationDto> genes){
        //获取全排列组成
        List<List<ApsOperationDto>> permutationsList = AlgorithmUtils.getPermutationsList(genes);
        List<List<ApsOperationDto>> result = new ArrayList<>();
        for (List<ApsOperationDto> apsOperationDtos : permutationsList) {
            boolean isValid = true;
            //按订单进行分组
            Map<String, List<ApsOperationDto>> orderCollect 
                    = apsOperationDtos.stream().collect(Collectors.groupingBy(i -> i.getOrderProcess().getOrderId()));
            //校验同一订单内 基因顺序是否正确
            for (var entry : orderCollect.entrySet()){
                if(!validateOrderSort(entry.getValue())){
                    //工序排序不对，弃用
                    isValid = false;
                    break;
                }
            }
            if(isValid){
                result.add(apsOperationDtos);
            }
        }
        //构造染色体
        List<Chromosome<ApsOperationDto>> chromosomes = new ArrayList<>();
        for (List<ApsOperationDto> apsOperationDtos : result) {
            //copy
            List<ApsOperationDto> copy = new ArrayList<>();
            for (ApsOperationDto apsOperationDto : apsOperationDtos) {
                ApsOperationDto copyO = apsOperationDto.deepCopy();
                copyO.setGeneCode();
                copy.add(copyO);
            }
            Chromosome<ApsOperationDto> chromosome = new Chromosome<>(copy);
            //构造染色体编码 （防止 交叉、变异时生成重复染色体）
            chromosome.preparedChromosomeCode();
            chromosomes.add(chromosome);
        }
        return chromosomes;
    }

    //判断订单内的工序是否正确
    private boolean validateOrderSort(List<ApsOperationDto> genes){
        boolean valid = true;
        for (int i = 0; i < genes.size() -1; i++) {
            if (genes.get(i).getOrderProcess().getSequence() > genes.get(i + 1).getOrderProcess().getSequence()) {
                valid = false;
                break;
            }
        }
        return valid;
    }

    /**
     * 随机抽取 返回对应染色体种群
     * 1、返回的染色体数目 应该  <= _populationSize
     * 2、需要保证有 >=1 个染色体 的正确的解 （满足硬约束【工序顺序】的解）
     * @param genes
     * @return
     */
    private List<Chromosome<ApsOperationDto>> generateRandomPopulation(List<ApsOperationDto> genes){
        List<Chromosome<ApsOperationDto>> population = new ArrayList<>();
        Set<String> usedCodes = new HashSet<>();
        SecureRandom random = new SecureRandom();

        for (int i = 0; i < super._population_size; i++) {
            // 使用保持工序顺序的随机排序方法
            List<ApsOperationDto> shuffled = shufflePreservingOrder(genes, random);

            Chromosome<ApsOperationDto> chromosome = new Chromosome<>(shuffled);
            chromosome.preparedChromosomeCode();

            // 避免重复染色体
            if (!usedCodes.contains(chromosome.getChromosomeCode())) {
                population.add(chromosome);
                usedCodes.add(chromosome.getChromosomeCode());
            } else {
                // 如果重复，重新生成
                i--;
            }
        }

        log.info("随机策略生成{}个染色体", population.size());
        return population;
    }

    /**
     * 随机打乱工序顺序但保持每个订单内工序的正确顺序
     *
     * 策略：
     * 1. 按订单分组工序并确保每个订单内工序顺序正确
     * 2. 将所有工序随机打乱，但通过约束确保同一订单的工序顺序正确
     * 3. 使用插入排序的思想，确保工序顺序约束
     *
     * @param geneSequence 要打乱的工序列表
     * @param random 随机数生成器
     * @return 打乱后的工序列表
     */
    private List<ApsOperationDto> shufflePreservingOrder(List<ApsOperationDto> geneSequence, Random random) {
        if (geneSequence == null || geneSequence.isEmpty()) {
            return new ArrayList<>();
        }

        // 1. 按订单分组工序并确保每个订单内工序顺序正确
        Map<String, List<ApsOperationDto>> orderOperationsMap = new HashMap<>();
        for (ApsOperationDto operation : geneSequence) {
            String orderId = operation.getOrderProcess().getOrderId();
            orderOperationsMap.computeIfAbsent(orderId, k -> new ArrayList<>()).add(operation);
        }

        // 2. 对每个订单内的工序按顺序排序（确保工序顺序正确）
        for (List<ApsOperationDto> operations : orderOperationsMap.values()) {
            operations.sort(Comparator.comparingInt(i->i.getOrderProcess().getSequence()));
        }

        // 3. 收集所有工序
        List<ApsOperationDto> allOperations = new ArrayList<>();
        for (List<ApsOperationDto> operations : orderOperationsMap.values()) {
            allOperations.addAll(operations);
        }

        // 4. 使用约束随机排序：随机打乱但保持工序顺序约束
        List<ApsOperationDto> shuffledSequence = constrainedShuffle(allOperations, random);

//        log.debug("随机排序结果：{}", shuffledSequence.stream()
//                .map(op -> op.getOrderProcess().getOrderId() + "-" + op.getOrderProcess().getProcessName() + "(" + op.getOrderProcess().getSequence() + ")")
//                .collect(Collectors.joining(" -> ")));
                OutputUtils.output(shuffledSequence,"随机排序结果");
        return shuffledSequence;
    }

    /**
     * 约束随机排序
     * 随机打乱工序顺序，但确保同一订单的工序顺序正确
     *
     * @param operations 工序列表
     * @param random 随机数生成器
     * @return 打乱后的工序列表
     */
    private List<ApsOperationDto> constrainedShuffle(List<ApsOperationDto> operations, Random random) {
        List<ApsOperationDto> result = new ArrayList<>();
        List<ApsOperationDto> remaining = new ArrayList<>(operations);

        while (!remaining.isEmpty()) {
            // 随机选择一个工序
            int randomIndex = random.nextInt(remaining.size());
            ApsOperationDto selectedOperation = remaining.get(randomIndex).deepCopy();
            selectedOperation.setGeneCode();
            // 检查是否可以插入到当前位置
            if (canInsertOperation(result, selectedOperation)) {
                result.add(selectedOperation);
                remaining.remove(randomIndex);
            } else {
                // 如果不能插入，尝试插入到合适的位置
                insertOperationAtCorrectPosition(result, selectedOperation);
                remaining.remove(randomIndex);
            }
        }

        return result;
    }

    /**
     * 检查工序是否可以插入到当前位置
     *
     * @param currentSequence 当前序列
     * @param operation 要插入的工序
     * @return 是否可以插入
     */
    private boolean canInsertOperation(List<ApsOperationDto> currentSequence, ApsOperationDto operation) {
        String orderId = operation.getOrderProcess().getOrderId();
        int sequence = operation.getOrderProcess().getSequence();

        // 检查当前序列中是否已有同一订单的后续工序
        for (ApsOperationDto existingOp : currentSequence) {
            if (orderId.equals(existingOp.getOrderProcess().getOrderId()) &&
                    existingOp.getOrderProcess().getSequence() > sequence) {
                return false; // 不能插入，因为已有更早的工序
            }
        }

        return true;
    }

    /**
     * 将工序插入到正确的位置
     *
     * @param currentSequence 当前序列
     * @param operation 要插入的工序
     */
    private void insertOperationAtCorrectPosition(List<ApsOperationDto> currentSequence, ApsOperationDto operation) {
        String orderId = operation.getOrderProcess().getOrderId();
        int sequence = operation.getOrderProcess().getSequence();

        // 找到插入位置：在同一个订单的后续工序之前，在更早工序之后
        int insertIndex = 0;

        for (int i = 0; i < currentSequence.size(); i++) {
            ApsOperationDto existingOp = currentSequence.get(i);

            // 如果是同一个订单的工序
            if (orderId.equals(existingOp.getOrderProcess().getOrderId())) {
                if (existingOp.getOrderProcess().getSequence() >= sequence) {
                    // 当前工序应该在现有工序之前
                    insertIndex = i;
                    break;
                } else {
                    // 当前工序应该在现有工序之后
                    insertIndex = i + 1;
                }
            } else {
                // 不同订单的工序，继续寻找
                insertIndex = i + 1;
            }
        }

        currentSequence.add(insertIndex, operation);
    }

}
